# 导入库
import numpy as np
import pandas as pd
import math
from sklearn.preprocessing import MinMaxScaler
from sklearn.cluster import KMeans

from collections import Counter
from sklearn.model_selection import StratifiedKFold
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import cross_val_score
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score, recall_score, f1_score
import warnings
warnings.filterwarnings("ignore")
from sklearn.cluster import k_means
from scipy.spatial.distance import cdist


def W(a: float, b: float):
    
    if pow((a),0.5) > b:
        return b**2
    else:
        return 1

    

    
# def W(a: float, b: float):
#     # w3
#     denominator = 1 - a * b
#     if denominator == 0:
#         # 处理分母为零的情况，例如返回默认值
#         return 1  
#     else:
#         return min(1, (a + b - a * b) / denominator)
    
def calculate_center_and_radius(gb):    
    data_no_label = gb[:,:]
    center = data_no_label.mean(axis=0)
    radius = np.max((((data_no_label - center) ** 2).sum(axis=1) ** 0.5))
    return center, radius


def splits(gb_list, num, k=2):
    gb_list_new = []
    for gb in gb_list:
        p = gb.shape[0]
        if p < num:
            gb_list_new.append(gb)
        else:
            gb_list_new.extend(splits_ball(gb, k))
    return gb_list_new


def splits_ball(gb, k):
    ball_list = []
    len_no_label = np.unique(gb, axis=0)
    if len_no_label.shape[0] < k:
        k = len_no_label.shape[0]
    label = k_means(X=gb, n_clusters=k, n_init=1, random_state=8)[1]
    for single_label in range(0, k):
        ball_list.append(gb[label == single_label, :])
    return ball_list


def assign_points_to_closest_gb(data, gb_centers):
    assigned_gb_indices = np.zeros(data.shape[0])
    for idx, sample in enumerate(data):
        t_idx = np.argmin(np.sqrt(np.sum((sample - gb_centers) ** 2, axis=1)))
        assigned_gb_indices[idx] = t_idx
    return assigned_gb_indices.astype('int')


def fuzzy_similarity(t_data, sigma=0, k=2):
    t_n, t_m = t_data.shape
    gb_list = [t_data]
    num = np.ceil(t_n ** 0.5)
    while True:
        ball_number_1 = len(gb_list)
        gb_list = splits(gb_list, num=num, k=k)
        ball_number_2 = len(gb_list)
        if ball_number_1 == ball_number_2:
            break
    gb_center = np.zeros((len(gb_list), t_m))
    for idx, gb in enumerate(gb_list):
        gb_center[idx], _ = calculate_center_and_radius(gb)
    point_to_gb = assign_points_to_closest_gb(t_data, gb_center)
    point_center = np.zeros((t_n, t_m))
    for i in range(t_n):
        point_center[i] = gb_center[point_to_gb[i]]
    tp = 1 - cdist(point_center, point_center) / t_m
    tp[tp < sigma] = 0
    return tp



#属性的相似矩阵计算
# 输入一个特征：列向量
def SimMatrix(a: np.array):
    #a是传入的模糊决策表的一列,二元数组n行1列
    # 1.计算a的标准差
    n = a.shape[0]
    # np.var() 方差
    a_std = (a.var() * n / (n - 1)) ** 0.5
    # 2.算关系矩阵对应位置的值
    sim=[]
    for i in range(n):
        for j in range(n):
            sim.append(max(
                round(
#                      min((a[j] - a[i] + a_std) /a_std, (a[i] - a[j] + a_std) / a_std)
                    min((a[j,0] - a[i,0] + a_std) /a_std, (a[i,0] - a[j,0] + a_std) / a_std)
                ,3 ), 0))
    # 将列表变为方阵
    length = int(math.sqrt(len(sim)))
    return np.array(sim).reshape((length,length))










#属性的组合函数
def combinAttribute(a: np.array, b: np.array):
    #a b 都是关系矩阵
    a_b=[]
    # 遍历行
    for i in range(0,a.shape[0]):
        # 遍历列
        for j in range(0,a.shape[1]):
            a_b.append(max(a[i][j]+b[i][j]-1,0))
    length = int(math.sqrt(len(a_b)))
    return np.array(a_b).reshape((length,length))


def U_XY(class_list: list, y: int):
    """
    计算样本在某个类别下的隶属度
    class_list: 类别包含的数据
    y: 样本序号
    """
    if y in class_list:
        return 1
    else:
        return 0.9 #(重叠函数)
        #return 0.8  #(非重叠函数)


#计算样本x的下近似
def URA_XX(a: np.array, class_list: list, x: int):
    """
    a: 二维数组，某属性或属性组合的模糊相似矩阵
    class_list: 类别包含的数据
    x:样本序号
    """
    I_list = []
    for i in range(0, a.shape[0]):
        #属性导出的模糊相似矩阵某一位置的值
        u = a[x][i]
        #对决策属性导出的某个类的隶属度
        v = U_XY(class_list=class_list, y=i)
        #经过模糊蕴涵聚合
        I_list.append(W(u, v))
        #返回下近似的值
    return min(I_list)


#计算模糊正域
def UPOSRAQ(a: np.array, classes_list: list, x: int):
    """
    计算某个样本x在属性a下的Q={X1,...,Xn}的最大
    a: 二维数组，是单个属性或者属性组合后的关系矩阵
    classes_list: 所有类别 二维数组
    x:样本序号
    """
    result_list = []
    for class_list in classes_list:
        result_list.append(URA_XX(a=a, class_list=class_list, x=x))
    return max(result_list)


def gamaAQ(a: np.array, classes_list: list):
    """
    计算决策属性在某个条件属性（组）的依赖程度
    a: 二维数组，是单个属性或者属性组合后的一个关系矩阵，代表了属性
    classes_list: 所有类别 二维数组
    """
    result_list = []
    for x in range(0, a.shape[0]):
        result_list.append(UPOSRAQ(a=a, classes_list=classes_list, x=x))
    return sum(result_list) / a.shape[0]


#算法开始前的数据转化
#导入数据一个数组
#按顺序计算每个属性的相似矩阵，存入一个list中方便后面取出使用
def AllMatrix(a:np.array):
    """
    a: 二维数组，是要处理的决策表
    
    """
    result = []
    #取出每一列计算
    #这里的减一是因为有一列是决策属性（类别），为方便起见，在处理数据时，把条件属性放在前面，决策属性放在最后。
    for i in range(0,a.shape[1]-1):
        result.append(SimMatrix(a[:,i:i+1]))
    return result




#算法开始
#导入数据一个数组
#计算每个属性的相似矩阵
def main(data:list, classes_list: list):
    #data:list对应存储所有属性相似矩阵的列表
    #候选集R
    R = []
    #记录上一次最大的依赖函数值
    Rbest = 0, 0
    #保存上一次候选集中属性组合后得到的模糊相似矩阵
    saveMatrix = np.array([1,1])
    while 1:
        #如果候选集R为空，则表示还没有属性被选择
        #则计算单个属性的相对依赖度
        #选择其中依赖度最大的那个属性添加到候选集R中
        if len(R) == 0:
            #当前循环某一步内获得最大依赖度的值
            maxgama = 0
            #用来记录当前循环某一步内获得最大依赖度的属性的序号
            mark = -1
            for i in range(0,len(data)):
                a=gamaAQ(a=data[i], classes_list=classes_list)
                #如果大就更新
                if(maxgama <= a):
                    maxgama = a
                    mark = i
            #把最大的属性序号加入到候选集R中
            #更新参与下次运算的矩阵
            R.append(mark)
            saveMatrix = data[mark]
            Rbest = maxgama
        else:
            
            #当候选集里有属性的时候，需要选择剩下的属性来添加
            #此时要计算两个或者更多属性的并后的依赖度，
            #此时假设我们将上一次并好的属性的矩阵保存了起来记为saveMatrix
            #只需用它来和剩余的属性进行并的运算（属性组合），用并后的结果来算依赖度
            #并选出最大值，进行判断，如果他与上一轮选出的属性的依赖度相等或是值为1
            #那么就停止算法；如果他比上一轮选出的属性依赖度大就把他添加到候选集里
            #参加下一次的候选计算
            
            #用来记录获得最大依赖度的属性的序号
            maxgama = 0
            mark = -1
            max_Matrix = np.array([1,1])
            for i in range(0,len(data)):
                #只计算剩余的属性，把属性并的中间结果记录下来为mid
                if i not in R:
                    mid = combinAttribute(a=saveMatrix, b=data[i])
                    a = gamaAQ(a=mid, classes_list=classes_list)
                    if(maxgama <= a):
                        maxgama = a
                        mark = i
                        max_Matrix = mid
            #循环结束得到依赖度最大的属性，进行判断
            #如果最大的依赖度比上一次大且没达到1就更新参数，
            #包括把属性添加到候选集、更新最大依赖度、更新参与计算的属性的并
            #继续下一轮的属性选择
            #if(maxgama > Rbest and maxgama != 1):
            if((maxgama > Rbest) and (maxgama != 1.0)):
                print("1",maxgama)
                R.append(mark)
                Rbest = maxgama
                saveMatrix = max_Matrix
                print(R)
            elif(maxgama == Rbest):
                print("2",maxgama)
                print(R)
                return R
            elif(maxgama == 1.0):
                print("3",maxgama)
                R.append(mark)
                print(R)
                return R
            


#测试-例子1
data = [-0.4, -0.3, -0.5, 0, -0.4, 0.2, -0.1, 1, -0.3, -0.4, -
        0.3, 0, 0.3, -0.3, 0, 1, 0.2, -0.3, 0, 1, 0.2, 0, 0, 0]
data = np.array(data).reshape((6,4))
classes_list = [[0,2,5], [1,3,4]]
sample = AllMatrix(data)
main(data=sample, classes_list=classes_list)


print("-----------------测试第二项---------------------")
print("-----------------------------------------------")
print("-----------------------------------------------")
print("-----------------------------------------------")

#读入数据集wine
dataset1 = pd.read_csv(r"C:\Users\Administrator\Desktop\压缩包\2023-12-23\2024-5-13\513code\processed_wine.csv", index_col=0)  # 读数据
dataset1

#以属性"Class"分组，把标签为1的一组放到列表中
dataset1.groupby("Class").get_group(1).index.to_list()

# 第一类lass_1
class_1 = dataset1.groupby("Class").get_group(1).index.to_list()
# 第二类class_2
class_2 = dataset1.groupby("Class").get_group(2).index.to_list()
# 第三类class_3
class_3 = dataset1.groupby("Class").get_group(3).index.to_list()
# 所有类别
classes_list1 = [class_1, class_2, class_3]
classes_list1

minMax = MinMaxScaler() #归一化
guiyihua = minMax.fit_transform(dataset1.iloc[0:, 0:13])
# print(guiyihua)

new_column = dataset1.iloc[0:, 13].values.reshape(-1, 1)
# 进行拼接
combined_data = np.hstack((guiyihua, new_column))
print(combined_data)
print(combined_data.shape)
print("-----------------------------------------------")
print("-----------------------------------------------")
print("-----------------------------------------------")
# # 读格式时的dataset1是Pandas中的Dataframe格式，此时转换成numpy中的array格式
# array_data = np.array(dataset1.iloc[:,0:])
# print(array_data.shape)
# print(array_data)

array_data = np.array(combined_data)
# #用算法约简数据集1
sample = AllMatrix(array_data)
main(data=sample, classes_list=classes_list1)